home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
cpp_libs
/
intrvews
/
xgrab.lha
/
xgrab
/
grabst
/
layout.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-03-06
|
31KB
|
1,236 lines
/**
GRAB Graph Layout and Browser System
Copyright (c) 1986, 1988 Regents of the University of California
Copyright (c) 1989, Tera Computer Company
**/
/**
layout.c contains procedures to position the vertices on each level of
a digraph.
**/
#include "malloc.h"
#include "attribute.h"
#include "digraph.h"
#include "debug.h"
#define MIN_X 0 /* minimum x-position */
#define MAX_X 10000 * HALF_CHAR /* maximum x-position */
#define MIN_OFFSET 20 * HALF_CHAR /* minimum offset between levels */
#define MAX_OFFSET 2 * MIN_OFFSET /* maximum offset between levels */
#define MAX_OFFSET_PERCENT 0.34
#define MAX_PRIORITY MAXNODES
#define PARTIAL 1 /* partial layout of level (dummies done) */
#define FULL 2 /* full layout, including dummies */
typedef struct plist PLIST; /* priority list of members */
struct plist
{
MEMBER *member;
PLIST *next;
};
#define each_member_by_priority(plist, member) \
{ PLIST *p;\
for (p = plist; p != NULL; p = p->next)\
{\
member = p->member;
#define abs(_x) \
( (_x) > 0 ? (_x) : -(_x) )
PLIST *sort_by_priority();
NODE *prev_dummy();
NODE *next_dummy();
float up_down_bc();
BOOL do_straighten(), do_vertical();
layout_levels(digraph)
DIGRAPH *digraph;
/**
layout_levels uses the priority layout method to determine the x-
coordinates of each member of each level of digraph.
**/
{
fix_all_positions(digraph); /* determine the initial positions */
straighten_all_dummies(digraph); /* line up dummy nodes vertically */
compute_barycenters(digraph); /* compute new barycenters */
compute_priorities(digraph); /* compute the priorities of each node */
if (debug)
{
printf("layout_levels:\n");
offset_levels(digraph); /* determine offset between levels */
compute_y_positions(digraph); /* put y value into each node */
}
layout_by_bc(digraph, UPDOWN, PARTIAL, 1);
/* layout levels 1..last by up-bc */
layout_one_level(digraph, First_level(digraph), PARTIAL, DOWN);
/* adjust top level */
layout_one_level(digraph, Last_level(digraph), PARTIAL, UP);
/* adjust bottom level */
offset_levels(digraph); /* determine offset between levels */
compute_y_positions(digraph); /* put y value into each node */
straighten_all_edges(digraph, straightenEdges);
/* remove bends in long edges */
} /* layout_levels */
quick_layout_levels(digraph)
DIGRAPH *digraph;
/**
quick_layout_levels does the minimal possible layout of the digraph by
spreading out the nodes on each level and by determining the y positions
of each level and node.
**/
{
fix_all_positions(digraph); /* determine the initial positions */
offset_levels(digraph); /* determine offset between levels */
compute_y_positions(digraph); /* put y value into each node */
} /* quick_layout_levels */
mid_level(digraph)
DIGRAPH *digraph;
/**
mid_level returns the level number of the middle level of digraph.
**/
{
return(Lno(Last_level(digraph)) / 2);
} /* mid_level */
fix_all_positions(digraph)
DIGRAPH *digraph;
/**
fix_all_positions fixes the positions of each node in digraph by
spacing the nodes according to their widths.
**/
{
LEVEL *level; /* each level */
int x_max; /* maximum x in digraph */
int x_level; /* maximum x on a level */
x_max = 0;
each_level(digraph, level)
loop
x_level = fix_positions(level);
if (x_level > x_max)
{
x_max = x_level;
}
endloop
each_level(digraph, level)
loop
spread_positions(level, x_max);
endloop
} /* fix_all_positions */
fix_positions(level)
LEVEL *level;
/**
fix_positions fixes the positions of each node in level by
spacing the nodes according to their widths. It returns
the x-value of the last node (including gap on right side).
**/
{
MEMBER *member; /* each member */
int prev_pos; /* each previous position */
prev_pos = MIN_X;
each_member(level, member)
loop
X_position(member) = prev_pos + GAP/2 + Half_width(member);
prev_pos = X_right(member) + GAP/2;
endloop
return (prev_pos); /* return largest x on level */
} /* fix_positions */
spread_positions(level, x_max)
LEVEL *level;
int x_max; /* maximum x-value of level */
/**
spread_positions spreads the nodes in each level evenly between 0
and x_max, starting at the left. If the optimal space is already
occupied by a previous node, the current node is moved as far left
as possible.
**/
{
MEMBER *member; /* each member */
int prev_pos; /* each previous position */
int intervals; /* number of divisions on the level */
int i; /* counter */
intervals = card(Members(level)) + 1;
prev_pos = MIN_X;
i = 0;
each_member(level, member)
loop
i += 1;
X_position(member) = MAX(((x_max * i) / intervals),
(prev_pos + GAP/2 + Half_width(member)));
prev_pos = X_right(member) + GAP/2;
endloop
} /* spread_positions */
compute_priorities(digraph)
DIGRAPH *digraph;
/**
compute_priorities computes the up, down, and updown priorities for each
node of each level of digraph.
**/
{
LEVEL *level; /* each level */
MEMBER *member; /* each member */
if (debug)
{
printf("\ncompute_priorities:\n");
}
each_level(digraph, level)
loop
each_member(level, member)
loop
if (Is_dummy(member))
/* give it the highest priority */
{
Priority(member, UP) = MAX_PRIORITY;
Priority(member, DOWN) = MAX_PRIORITY;
}
else
{
Priority(member, UP) = card(Ante_set(Member_node(member)));
Priority(member, DOWN) = card(Succ_set(Member_node(member)));
}
/* updown priority is just the average of the two */
Priority(member, UPDOWN) = (Priority(member, UP) +
Priority(member, DOWN)) / 2;
if (debug)
{
printf(" %s: (%d, %d %d)\n", Name(Member_node(member)),
Priority(member, UP), Priority(member, DOWN),
Priority(member, UPDOWN));
}
endloop
endloop
} /* compute_priorities */
layout_by_bc(digraph, direction, mode, levnum)
DIGRAPH *digraph;
DIRECTION direction;
int mode; /* FULL or PARTIAL layout mode */
int levnum;
/**
layout_by_bc positions the nodes in each level of digraph as close to its
up (direction == UP) or down (direction == DOWN) barycenters as possible.
The bc's of the previous and next levels of each level are recomputed
if the positions of nodes are changed. Levnum is first (direction = UP)
or last (direction = DOWN) level to layout.
**/
{
LEVEL *level; /* each level */
switch (direction)
{
case UP: /* layout from given level to last, by up barycenters */
each_level(digraph, level)
loop
if (Lno(level) >= levnum) /* layout this level */
{
layout_one_level(digraph, level, mode, UP);
}
endloop
break;
case DOWN: /* layout from last level to given, by down barycenters */
each_level_reverse(digraph, level)
loop
if (level == Last_level(digraph))
{
continue;
}
if (Lno(level) >= levnum) /* layout this level */
{
layout_one_level(digraph, level, mode, DOWN);
}
endloop
break;
case UPDOWN: /* layout first to last to first, by average bc */
each_level(digraph, level)
loop
layout_one_level(digraph, level, mode, UPDOWN);
endloop
each_level_reverse(digraph, level)
loop
layout_one_level(digraph, level, mode, UPDOWN);
endloop
break;
}
if (debug)
{
printf("\nlayout_by_bc: %s\n",
(direction == UP) ? "UP" : ((direction == DOWN) ?
"DOWN" : "UPDOWN"));
offset_levels(digraph); /* determine offset between levels */
compute_y_positions(digraph); /* put y value into each node */
}
} /* layout_by_bc */
layout_one_level(digraph, level, mode, direction)
DIGRAPH *digraph;
LEVEL *level;
int mode; /* FULL or PARTIAL layout (if dummies already laid out) */
DIRECTION direction;
/**
layout_one_level positions the nodes in level as close as possible to
their up (direction == UP) or down (direction == down) barycenters.
The bc's of the previous and next levels of level are recomputed.
**/
{
MEMBER *member; /* each member */
PLIST *plist; /* members sorted by priority */
int shift; /* amount to shift each member */
float bc;
plist = sort_by_priority(level, direction);
/**
NOTE: the above is incredibly wasteful, since this is done
on each iteration, and the priorities are static.
**/
each_member(level, member)
loop
if (mode == FULL)
{
Is_layed(member) = FALSE; /* not layed out yet */
}
else
{
if (Is_dummy(member) &&
(Is_dummy(prev_dummy(digraph, member)) ||
Is_dummy(next_dummy(digraph, member))) )
{
Is_layed(member) = TRUE;
}
else
{
Is_layed(member) = FALSE;
}
}
endloop
each_member_by_priority(plist, member)
loop
if (direction == UPDOWN)
{
bc = up_down_bc(member);
}
else
{
bc = Bc(member, direction);
}
if (bc == NO_BC) /* leave it alone */
{
continue;
}
if (Is_layed(member))
{
continue;
}
shift = (int) bc - X_position(Member_node(member));
if (shift < 0) /* shift left */
{
shift_left(member, (int) bc);
}
else if (shift > 0) /* shift right */
{
shift_right(member, (int) bc);
}
Is_layed(member) = TRUE; /* layed out now */
if (debug)
{
printf("\nlayout_one_level: layed out %s: ",
Name(Member_node(member)));
printf("Bc(%s) = %1.2f, X_position = %d\n",
(direction == UP) ? "UP": ((direction == DOWN) ?
"DOWN" : "UPDOWN"),
Bc(member, direction % 2), X_position(member));
if (Prev_level(level) != NULL)
/* recompute down-bc of prev level */
{
compute_bc_level(digraph, Prev_level(level), DOWN);
}
if (Next_level(level) != NULL) /* recompute up-bc of next level */
{
compute_bc_level(digraph, Next_level(level), UP);
}
offset_levels(digraph); /* determine offset between levels */
compute_y_positions(digraph); /* put y value into each node */
}
endloop
dispose_plist(plist); /* recycle */
/* now recompute bc's of affected levels */
if (Prev_level(level) != NULL) /* recompute down-bc of prev level */
{
compute_bc_level(digraph, Prev_level(level), DOWN);
}
if (Next_level(level) != NULL) /* recompute up-bc of next level */
{
compute_bc_level(digraph, Next_level(level), UP);
}
} /* layout_one_level */
shift_left(member, new_x)
MEMBER *member; /* member to shift */
int new_x; /* new position for member */
/**
shift_left shifts member left to new_x. If The left is blocked by a
layed-out member, then member is moved as close as possible to new_x.
**/
{
MEMBER *blocker; /* layed-out member to left */
MEMBER *first; /* first member to shift left */
MEMBER *prev; /* each previous member on level */
int min_x; /* minimum left position */
int prev_pos; /* position of each previous member */
/* first find the member to the left with a higher priority */
for (first = member, blocker = Prev_member(member);
(blocker != NULL) && !Is_layed(blocker);
first = blocker, blocker = Prev_member(blocker))
{}
if (blocker == NULL) /* shift as little as possible */
{
min_x = MIN_X + GAP/2;
}
else /* stop at blocker */
{
min_x = X_right(blocker) + GAP/2;
}
/* now shift all previous members left as much as possible */
prev_pos = min_x; /* initial position */
for (prev = first; prev != Next_member(member); prev = Next_member(prev))
{
X_position(prev) = prev_pos + GAP/2 + Half_width(prev);
prev_pos = X_right(prev) + GAP/2;
}
/**
We may have just shifted too far. Shift members back to the
right if possible
**/
if (X_position(member) < new_x)
{
X_position(member) = new_x;
prev_pos = X_left(member) - GAP/2;
for (prev = Prev_member(member); prev != blocker;
prev = Prev_member(prev))
{
X_position(prev) = prev_pos - GAP/2 - Half_width(prev);
prev_pos = X_left(prev) - GAP/2;
}
}
} /* shift_left */
shift_right(member, new_x)
MEMBER *member; /* member to shift */
int new_x; /* new position for member */
/**
shift_right shifts member right to new_x. If the right is blocked by a
layed-out member, then member is moved as close as possible to new_x.
**/
{
MEMBER *blocker; /* first layed-out member to the right */
MEMBER *first; /* first member to shift right */
MEMBER *next; /* each next member on level */
int max_x; /* maximum right position */
int prev_pos; /* position of each previous member */
/* first find the member to the right with a higher priority */
for (first = member, blocker = Next_member(member);
(blocker != NULL) && !Is_layed(blocker);
first = blocker, blocker = Next_member(blocker))
{}
if (blocker == NULL) /* stop at right edge */
{
max_x = MAX_X - GAP/2;
}
else /* stop at blocker */
{
max_x = X_left(blocker) - GAP/2;
}
/* now shift all next members right as much as possible */
prev_pos = max_x;
for (next = first; next != Prev_member(member); next = Prev_member(next))
{
X_position(next) = prev_pos - Half_width(next) - GAP/2;
prev_pos = X_left(next) - GAP/2;
}
/**
We may have just shifted too far. Shift members back to the left
if possible
**/
if (X_position(member) > new_x)
{
X_position(member) = new_x;
prev_pos = X_right(member) + GAP/2;
for (next = Next_member(member); next != blocker;
next = Next_member(next))
{
X_position(next) = prev_pos + GAP/2 + Half_width(next);
prev_pos = X_right(next) + GAP/2;
}
}
} /* shift_right */
PLIST *sort_by_priority(level, direction)
LEVEL *level;
DIRECTION direction;
/**
sort_by_priority returns a list of the members in level sorted by
priority.
**/
{
PLIST *plist; /* the priority list */
PLIST *prev, *cur; /* previous and current plist position */
MEMBER *member; /* each member */
plist = NULL;
each_member(level, member)
loop
for (prev = NULL, cur = plist; (cur != NULL) &&
(Priority(cur->member, direction) > Priority(member, direction));
prev = cur, cur = cur->next)
{}
new(cur, PLIST);
cur->member = member;
if (prev == NULL) /* add to the head of the list */
{
cur->next = plist;
plist = cur;
}
else /* add after prev */
{
cur->next = prev->next;
prev->next = cur;
}
endloop
if (debug)
{
printf("\nsort_by_priority: (%s)\n",
(direction == UP) ? "UP" : ((direction == DOWN) ?
"DOWN" : "UPDOWN"));
each_member_by_priority(plist, member)
loop
printf(" %s: %d\n", Name(Member_node(member)),
Priority(member, direction));
endloop
}
return(plist);
} /* sort_by_priority */
dispose_plist(plist)
PLIST *plist;
/**
dispose_plist disposes of each part of plist.
**/
{
PLIST *next; /* next to dispose */
while (plist != NULL)
{
next = plist->next;
dispose(plist);
plist = next;
}
} /* dispose_plist */
show_layout(digraph)
DIGRAPH *digraph;
/**
show_layout prints the members in each level of digraph using their
computed positions.
**/
{
int column, left_pos;/* counters */
LEVEL *level; /* each level */
MEMBER *member; /* each member */
printf("\n\nDigraph Levels (positioned):\n");
each_level(digraph, level)
loop
column = 0;
each_member(level, member)
loop
for (; column < X_left(member); column++)
{
printf(" ");
}
if (column == X_left(member))
{
printf(".");
column++;
}
left_pos = X_position(member) - strlen(Text(Member_node(member)))/2;
for (; column < left_pos; column++)
{
printf("_");
printf("%s", Text(Member_node(member)));
}
for (column += strlen(Text(Member_node(member)));
column < X_right(member); column++)
{
printf("_");
}
printf(".");
column++;
endloop
printf("\n\n");
endloop
} /* show_layout */
show_layout_numbers(digraph)
DIGRAPH *digraph;
/* another debugging routine */
{
LEVEL *level;
MEMBER *member;
each_level(digraph, level)
loop
each_member(level, member)
loop
printf("%s(%d, %d)@(%d, %d)BC(Up:%d/Down:%d) ",
Text(Member_node(member)),
2 * Half_width(member), 2 * Half_height(member),
X_position(member), Y_position(member),
(int) Bc(member, UP), (int) Bc(member, DOWN));
endloop;
printf("\n");
endloop;
} /* show_layout_numbers */
offset_levels(digraph)
DIGRAPH *digraph;
/**
offset_levels computes the offset of each level of digraph. This is done
by accounting for the longest arc between two levels.
**/
{
LEVEL *level; /* each level */
MEMBER *member; /* each member */
VNO vno; /* each antecedent */
int x, y; /* right triangle parameters */
float minoffset, maxoffset; /* min and max offsets in graph */
float alpha, scale, shift;
minoffset = maxoffset = -1.0;
each_level(digraph, level)
loop
Offset(level) = 0; /* no offset yet */
if (Prev_level(level) == NULL) /* no previous level */
{
each_member(level, member)
loop
y = Half_height(member);
if (y > Offset(level)) /* new maximum */
{
Offset(level) = y;
}
endloop;
continue;
}
each_member(level, member)
loop
each_element(Ante_set(Member_node(member)), vno)
loop
x = abs(X_position(Member(digraph, vno)) - X_position(member));
/* offset should be at least 2/3 x value */
y = (2*(x + 1)) / 3 + Half_height(Member(digraph,vno)) +
Half_height(member);
if (y > Offset(level)) /* new maximum */
{
Offset(level) = y;
}
endloop
endloop
if (Offset(level) < MIN_OFFSET) /* use default minimum */
{
Offset(level) = MIN_OFFSET;
}
if (Offset(level) < minoffset || minoffset == -1)
{
minoffset = Offset(level);
}
if (Offset(level) > maxoffset || maxoffset == -1)
{
maxoffset = Offset(level);
}
endloop
/* Compress levels if the range of level offsets is too big. */
if ((maxoffset - minoffset) / minoffset > MAX_OFFSET_PERCENT)
{
alpha = (maxoffset - (MAX_OFFSET_PERCENT + 1) * minoffset) /
(MAX_OFFSET_PERCENT + 2);
scale = (maxoffset - minoffset - 2 * alpha) / (maxoffset - minoffset);
shift = (minoffset + alpha) - scale * minoffset;
each_level(digraph, level)
loop
if (Prev_level(level) == NULL)
{
continue;
}
Offset(level) = scale * (float) Offset(level) + shift;
endloop;
}
} /* offset_levels */
compute_y_positions(digraph)
DIGRAPH *digraph;
/**
compute_y_positions uses the offset for each level to determine
the y value to put in each node.
**/
{
LEVEL *level; /* each level */
MEMBER *member; /* each member */
int y; /* y value for each level */
y = MIN_OFFSET;
each_level_reverse(digraph, level)
loop
each_member(level, member)
loop
Y_position(member) = y;
endloop
y += Offset(level); /* y value for level */
endloop
} /* compute_y_positions */
straighten_all_dummies(digraph)
DIGRAPH *digraph;
/**
straighten_all_dummies attempts to position dummy nodes in a vertical
line
**/
{
NODE *node;
LEVEL *level;
int i;
each_node(digraph, node)
loop
if (Is_dummy(node))
{
continue;
}
straighten_dummies(digraph, node);
endloop
for (i=0; i<3; i++)
{
each_level(digraph, level)
loop
adjust_positions(digraph, level, UP);
endloop
each_level_reverse(digraph, level)
loop
adjust_positions(digraph, level, DOWN);
endloop
}
} /* straighten_all_dummies */
straighten_dummies(digraph, node)
DIGRAPH *digraph;
NODE *node;
/**
straighten_dummies follows the sequence of dummy nodes from each normal
node and changes the x-positions to be the minimum value of all the dummy
nodes in the sequence.
-> Changed 2/12/86 by ebm, so that x-position of dummy closest to node
starting chain is used.
**/
{
NODE *next;
VNO vno;
int x_min;
int x_start;
x_start = X_position(node);
each_element(Succ_set(node), vno)
loop
next = Node(digraph, vno);
if (!Is_dummy(next))
{
continue;
}
x_min = X_position(next);
do
{
if (abs(X_position(next) - x_start) < abs(x_min - x_start))
{
x_min = X_position(next);
}
next = next_dummy(digraph, next);
} while (Is_dummy(next));
next = Node(digraph, vno);
do
{
X_position(next) = x_min;
next = next_dummy(digraph, next);
} while (Is_dummy(next));
endloop
} /* straighten_dummies */
NODE *prev_dummy(digraph, node)
DIGRAPH *digraph;
NODE *node;
/**
prev_dummy returns a pointer to the next dummy node in the sequence
If node is not a dummy node, this could give unexpected results
**/
{
VNO vno;
each_element(Ante_set(node), vno)
loop
return(Node(digraph, vno)); /* return the first ancestor found */
endloop
return (NULL); /* error, no ancestor found */
} /* prev_dummy */
NODE *next_dummy(digraph, node)
DIGRAPH *digraph;
NODE *node;
/**
next_dummy returns a pointer to the next dummy node in the sequence
If node is not a dummy node, this could give unexpected results.
**/
{
VNO vno;
each_element(Succ_set(node), vno)
loop
return(Node(digraph, vno)); /* return the first successor found */
endloop
return (NULL); /* error, no successor found */
} /* next_dummy */
adjust_positions(digraph, level, direction)
DIGRAPH *digraph;
LEVEL *level;
DIRECTION direction;
/**
adjust_positions moves nodes to the right if necessary to avoid
conflicting with the new positions of the dummy nodes.
**/
{
MEMBER *member;
int prev_pos;
int x_pos;
NODE *ante_node;
NODE *succ_node;
prev_pos = MIN_X;
each_member(level, member)
loop
x_pos = MAX(X_position(member),
(prev_pos + GAP/2 + Half_width(member)));
if (Is_dummy(member) && direction == UP)
{
ante_node = prev_dummy(digraph, member);
if (Is_dummy(ante_node))
{
x_pos = MAX(x_pos, X_position(ante_node));
}
}
if (Is_dummy(member) && direction == DOWN)
{
succ_node = next_dummy(digraph, member);
if (Is_dummy(succ_node))
{
x_pos = MAX(x_pos, X_position(succ_node));
}
}
X_position(member) = x_pos;
prev_pos = X_right(member) + GAP/2;
endloop
} /* adjust_positions */
float up_down_bc(member)
MEMBER *member;
/* up_down_bc returns the average barycenter of the member */
{
float bc, bc_up, bc_down;
int up_count, down_count;
up_count = card(Ante_set(member));
down_count = card(Succ_set(member));
bc_up = Bc(member, UP);
bc_down = Bc(member, DOWN);
if ((up_count + down_count) == 0)
{
bc = 0;
}
else
{
bc = (up_count * bc_up + down_count * bc_down) /
(up_count + down_count);
}
return (bc);
}
straighten_all_edges(digraph, str_mode)
DIGRAPH *digraph;
int str_mode; /* straightening mode: bends allowed or not */
/**
straighten_all_edges attempts to draw all multi-segment edges as a small
number of straight line segments, without moving any regular nodes.
**/
{
LEVEL *level;
NODE *member, *first_dummy, *last_dummy, *next_node;
VNO vno;
each_level(digraph, level)
loop
if (debug3)
{
printf("level number %d : \n", Lno(level));
}
each_member(level, member)
loop
if (debug3)
{
printf(" scanning node %s \n", Name(member));
}
if (Is_dummy(member))
{
continue;
}
each_element(Succ_set(member), vno)
loop
next_node = Node(digraph, vno);
if (!Is_dummy(next_node)) /* must be dummy node */
{
continue;
}
first_dummy = next_node;
last_dummy = next_node;
while (Is_dummy(next_node)) /* find next normal node */
{
last_dummy = next_node;
next_node = next_dummy(digraph, next_node);
}
straighten_edge(digraph, member, first_dummy, last_dummy,
next_node, str_mode);
endloop
endloop
endloop
} /* straighten_all_edges */
straighten_edge(digraph, node, first_dummy, last_dummy, next_node, straighten)
DIGRAPH *digraph;
NODE *node, *first_dummy, *last_dummy, *next_node;
BOOL straighten; /* straightening mode: if true, does a better job */
/**
straighten_edge straightens the multi-segment edge running from node via
first_dummy to next_node. No normal nodes are moved.
**/
{
NODE *s_node, *s_dummy, *t_node;
s_node = node; /* starting node */
s_dummy = first_dummy; /* first dummy after starting node */
t_node = next_node; /* ending node */
if (!straighten) /* do a half-hearted job */
{
if (do_straighten(digraph, s_node, s_dummy, t_node))
{
if (debug3)
{
printf("\tstraightened edge from %s to %s\n", Name(s_node),
Name(t_node));
}
}
}
else
{
/**
work backward, adding the longest vertical segment possible,
then a non-vertical segment, then try again.
This looks strange, but okay. Better schemes could be devised,
but will probably waste a lot of time
**/
for (;;)
{
if (do_vertical(digraph, s_node, t_node))
{
if (debug3)
{
printf("\tmade vertical edge from %s to %s\n", Name(s_node),
Name(t_node));
}
if (s_node == next_node)
{
t_node = last_dummy;
}
else
{
t_node = prev_dummy(digraph, s_node);
}
if (s_node == node || t_node == node) /* reached node, done */
{
return;
}
else
{
s_node = node; /* try to draw segment from node */
}
}
else /* back up a level */
{
if (s_node == node)
{
s_node = first_dummy;
}
else
{
s_node = next_dummy(digraph, s_node);
}
}
}
}
} /* straighten_edge */
BOOL do_straighten(digraph, s_node, s_dummy, t_node)
DIGRAPH *digraph;
NODE *s_node, *s_dummy, *t_node;
/**
do_straighten calculates a straight line between s_node and t_node and
then checks to see if the intervening dummy nodes could be moved to those
positions without conflicting with other nodes. If so, the x positions
of the dummy nodes are changed, and a TRUE value is returned. Otherwise,
the x positions are unchanged and a FALSE value is returned.
**/
{
int x; /* calculated straight line position of dummy */
int x_s, x_t; /* x positions of start and end nodes */
int y_s, y_t; /* y positions of start and end nodes */
int x_min, x_max; /* min and max dummy node positions */
float slope; /* slope (dx/dy) of straight line */
NODE *curr; /* current dummy node */
if (s_dummy == t_node) /* if single segment edge */
{
return (TRUE);
}
x_s = X_position(s_node);
x_t = X_position(t_node);
y_s = Y_position(s_node);
y_t = Y_position(t_node);
slope = ((float) (x_t - x_s)) / ((float) (y_t - y_s));
curr = s_dummy;
while (curr != t_node)
{
x = x_s + slope * (Y_position(curr) - y_s);
if (Prev_member(curr) == NULL)
{
x_min = MIN_X;
}
else
{
x_min = X_right(Prev_member(curr)) + GAP / 2;
}
if (Next_member(curr) == NULL)
{
x_max = MIN_X;
}
else
{
x_max = X_left(Next_member(curr)) - GAP / 2;
}
if (x - Half_width(curr) < x_min || x + Half_width(curr) > x_max)
/* if dummy runs into adj. node */
{
return (FALSE);
}
curr = next_dummy(digraph, curr);
}
curr = s_dummy;
while (curr != t_node)
{
X_position(curr) = (int) (x_s + slope * (Y_position(curr) - y_s));
/* change position of dummy */
curr = next_dummy(digraph, curr);
}
return (TRUE);
} /* do_straighten */
BOOL do_vertical(digraph, s_node, t_node)
DIGRAPH *digraph;
NODE *s_node, *t_node;
/**
do_vertical checks if you can draw a vertical line between s_node and
t_node If so, the x positions of the intervening nodes are changed, and
TRUE is returned. Otherwise, the x positions are unchanged and
FALSE is returned. If s_node or t_node is not a dummy, its x position
will not be changed. t_node's x position is never changed.
**/
{
int x; /* calculated straight line position of dummy */
int x_min, x_max; /* min and max dummy node positions */
NODE *curr; /* current dummy node */
if (!Is_dummy(s_node) && X_position(s_node) != X_position(t_node))
/* check if trying to change non-dummy */
{
return (FALSE);
}
x = X_position(t_node);
curr = s_node;
while (curr != t_node)
{
if (Prev_member(curr) == NULL)
{
x_min = MIN_X;
}
else
{
x_min = X_right(Prev_member(curr)) + GAP / 2;
}
if (Next_member(curr) == NULL)
{
x_max = MIN_X;
}
else
{
x_max = X_left(Next_member(curr)) - GAP / 2;
}
if (x - Half_width(curr) < x_min || x + Half_width(curr) > x_max)
/* if dummy runs into adj. node */
{
return (FALSE);
}
curr = next_dummy(digraph, curr);
}
curr = s_node;
while (curr != t_node)
{
X_position(curr) = x; /* change position of nodes */
curr = next_dummy(digraph, curr);
}
return (TRUE);
} /* do_vertical */